home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 008a / emslb221.zip / EMSLIB.DOC next >
Text File  |  1991-10-13  |  49KB  |  1,092 lines

  1.                                  EMSLIB
  2.           Version-independent C Interface to LIM EMS Functions
  3.                   for LIM EMS versions 3.0 and higher
  4.                           EMSLIB version 2.21
  5.                           by James W. Birdsall
  6.                                 10/13/91
  7.  
  8.  
  9. 0. CONTENTS
  10. -----------
  11.  
  12.    0.     CONTENTS
  13.    I.     INTRODUCTION
  14.     I.1    WHAT IS SUPPORTED
  15.     I.2    COPYRIGHT AND LICENSE
  16.    II.    COMPILING AND LINKING WITH THE LIBRARIES
  17.     II.1   WITH C
  18.     II.2   WITH C++
  19.     II.3   EMSTEST, THE EXAMPLE PROGRAM
  20.    III.   PROGRAMMING WITH EMSLIB
  21.     III.1  INITIALIZING THE LIBRARY
  22.     III.2  ORDINARY USE
  23.     III.3  FRAME CACHING
  24.     III.4  SAVE/RESTORE
  25.     III.5  OTHER TIPS
  26.    IV.    LIBRARY REFERENCE
  27.     IV.1   GLOBAL VARIABLES
  28.     IV.2   FUNCTIONS
  29.    V.     ERROR CODES
  30.     V.1    INTERNAL ERRORS
  31.     V.2    EMS DRIVER ERRORS
  32.    VI.    THE END
  33.     VI.1   ACKNOWLEDGEMENTS
  34.  
  35.  
  36. I. INTRODUCTION
  37. ---------------
  38.  
  39.    EMSLIB provides a high-level interface to LIM EMS control functions
  40. for common operations such as allocating, mapping, and freeing EMS, and
  41. copying data to and from EMS. The interface has been made independent of
  42. the version of the EMS driver as far as possible, so that parameters and
  43. returned data are always in the same format, but the EMS call most
  44. appropriate to the EMS version implemented by the driver is used.
  45.  
  46.    EMSLIB is written in assembly language for speed and assembled with
  47. Borland's Turbo Assembler (TASM) 2.5. The source code is not compatible
  48. with the Microsoft Assembler (MASM).
  49.  
  50.  I.1 WHAT IS SUPPORTED
  51.  ---------------------
  52.  
  53.    EMSLIB expressly supports the Lotus-Intel-Microsoft (LIM) Expanded
  54. Memory Specification (EMS) versions 3.0, 3.2, and 4.0. Versions above
  55. 4.0 are supported as 4.0. Versions below 3.0 are not supported.
  56. Save/restore is not supported under version 3.0, and assigning names to
  57. EMS handles is only supported under version 4.0. These exceptions to
  58. version-independence are due to limitations of the unsupported versions;
  59. the necessary services are not available from the driver.
  60.  
  61.    EMSLIB supports tiny, small, medium, compact, large, and huge memory
  62. models. The small model library supports both tiny and small models, so
  63. no library is provided specifically for tiny model.
  64.  
  65.    EMSLIB supports any version of Turbo C, Turbo C++, or Borland C++, in
  66. both C and C++ modes, and Microsoft C 6.00 and 6.00A. EMSLIB has been
  67. tested with Borland C++ 2.0 and Microsoft C 6.00A in all of the
  68. supported memory models. EMSLIB should work with earlier versions of
  69. Microsoft C and any other C compiler that 1) uses compatible parameter
  70. passing and return methods and 2) can use standard-format libraries.
  71.  
  72.  I.2 COPYRIGHT AND LICENSE
  73.  -------------------------
  74.  
  75.    EMSLIB is not in the public domain. All the files are copyright 1991
  76. by James W. Birdsall, all rights reserved. Permission is granted to do
  77. the following:
  78.  
  79.         You may freely redistribute this archive, so long as it contains
  80.         all the files listed in the file MANIFEST, intact and
  81.         unmodified.
  82.  
  83.         You may use the libraries in programs for your own use. You may
  84.         not distribute programs linked with these libraries.
  85.  
  86.    Payment of the $5 shareware registration fee ($50 for commercial use)
  87. grants the following license, in addition to the permissions listed
  88. above:
  89.  
  90.         You may request the source to EMSLIB. You may modify the source
  91.         as necessary for use in your programs. However, you may not
  92.         redistribute either the original or modified source.
  93.  
  94.         You may distribute programs linked with either the original
  95.         libraries or libraries generated from source you have modified,
  96.         without royalty.
  97.  
  98. For the purposes of this license, commercial use is defined as use by an
  99. incorporated entity in a software product that is regarded as the
  100. product of the corporation, no matter how the software product is
  101. distributed, but only if 100 or more copies of the product are expected
  102. to be made.
  103.  
  104.    The contents of the distribution archive, and all other related
  105. files, information, and services are provided as-is. While every effort
  106. has been made to ensure that the files, information, and services are
  107. accurate and correct, the author cannot be held liable for damages
  108. arising out of the use of or inability to use this product.
  109.  
  110.    Information on contacting the author is provided at the end of this
  111. file.
  112.  
  113.  
  114. II. COMPILING AND LINKING WITH THE LIBRARIES
  115. --------------------------------------------
  116.  
  117.    This section describes how to use the EMSLIB libraries with your
  118. programs.
  119.  
  120.    EMSLIB is provided as Borland/Microsoft standard library files.
  121. Libraries are provided for small, medium, compact, large, and huge memory
  122. models (tiny model uses the small model library). The model for which a
  123. library is intended is indicated by the last letter of the filename
  124. proper, which is the same as the first letter for the model. For
  125. example, EMSLIBL.LIB is the large model library.
  126.  
  127.  II.1 WITH C
  128.  -----------
  129.  
  130.    To use EMSLIB in C programs, you must #include the file EMSLIB.H in
  131. every source file that calls EMSLIB functions, accesses EMSLIB global
  132. variables, or uses #defined constants provided by EMSLIB.
  133.  
  134.    The procedures for linking EMSLIB with the rest of your program vary
  135. according to the compiler and method you are using. In general, you must
  136. include the appropriate library (the library corresponding to the memory
  137. model in which you have compiled the rest of your program) in the link.
  138.  
  139.    If you are compiling in the Integrated Development Environment of
  140. Turbo/Borland C[++], you should include the name of the appropriate
  141. library in the project file for your program. For example, if you are
  142. working in the compact memory model, you should include EMSLIBC.LIB in
  143. your project file.
  144.  
  145.    If you are using a command-line compiler (bcc, tcc, or cl) to compile
  146. and link, simply place the full name of the appropriate EMSLIB library
  147. on the command line. For example, "bcc -mc foo.c emslibc.lib" will
  148. compile the file "foo.c" and link it with emslibc.lib.
  149.  
  150.    If you are linking manually (using TLINK or LINK), place the name of
  151. the appropriate library in with the other libraries. For example,
  152.         tlink c0c.obj foo.obj, foo.exe, foo.map, cc.lib emslibc.lib
  153. or
  154.         link foo.obj, foo.exe, foo.map, emslibc.lib ;
  155. will link the object "foo.obj" with the appropriate startup object and
  156. standard library (LINK automatically includes the startup object and
  157. standard library, so it is not necessary to explicitly include them) and
  158. the compact model EMSLIB library.
  159.  
  160.  II.2 C++
  161.  --------
  162.  
  163.    To use EMSLIB in C++ programs, you must #include the file EMSLIB.HPP
  164. in every file that calls EMSLIB functions, accesses EMSLIB global
  165. variables, or uses #defined constants provided by EMSLIB. Be careful to
  166. include EMSLIB.HPP instead of EMSLIB.H. If you include the wrong one,
  167. you will probably see "undefined symbol" errors when linking.
  168.  
  169.    Otherwise, the procedures for using EMSLIB with C++ programs are the
  170. same as for using it with C programs.
  171.  
  172.  II.3 EMSTEST, THE EXAMPLE PROGRAM
  173.  ---------------------------------
  174.  
  175.    A large and complete example program and tester, EMSTEST, has been
  176. included in this distribution. It can be compiled in any of the
  177. supported memory models (although tiny model requires some contortions
  178. to do so), with either Borland or Microsoft compilers.
  179.  
  180.    The test program has three source files: EMSTEST.C, EMSTEST2.C, and
  181. EMSTEST3.C, and a header file EMSTEST.H. When compiling in tiny model
  182. with Borland compilers, an additional source file, STACK.ASM, is needed.
  183. STACK.OBJ, which is STACK.ASM pre-assembled, has been included for those
  184. who do not have assemblers.
  185.  
  186.    Example makefiles have been included for Borland and Microsoft
  187. compilers, EXMAKETC and EXMAKEMS respectively. Complete instructions for
  188. making the example program and using the example makefiles are included
  189. at the beginning of each makefile. More information about the program is
  190. also included at the beginning of EMSTEST.C.
  191.  
  192.    EMSTEST requires at least eight pages of available expanded memory
  193. and at least 210,000 bytes of available conventional memory to run.
  194.  
  195.  
  196. III. PROGRAMMING WITH EMSLIB
  197. ----------------------------
  198.  
  199.    This section describes how to make EMSLIB calls in your program, and
  200. details various tricks and tips which you may find useful.
  201.  
  202.  III.1 INITIALIZATION
  203.  --------------------
  204.  
  205.    The library initialization function EMMlibinit() _must_ be called
  206. before any other EMSLIB calls are made. All other EMSLIB functions are
  207. guaranteed to fail if called before EMMlibinit(). EMMlibinit()
  208. determines whether an EMS driver is present and sets up various internal
  209. and global variables necessary to the functioning of EMSLIB.
  210.  
  211.  III.2 ORDINARY USE
  212.  ------------------
  213.  
  214.    EMSLIB provides several sets of functions. First, there is a set of
  215. functions which are intended to be orthogonal with the standard C
  216. functions malloc() and free(), and the Turbo C[++] function coreleft().
  217. These functions are EMMalloc(), EMMfree(), and EMMcoreleft().
  218.  
  219.    Second, there is a group of functions for copying data to and from
  220. expanded memory. These functions allow you to treat allocated blocks of
  221. expanded memory pages as linear memory, hiding the details of page
  222. mapping. These functions are EMMcopyto(), EMMcopyfrom(), EMMicopyto(),
  223. EMMicopyfrom(), _EMMicopyto(), and _EMMicopyfrom().
  224.  
  225.    Third, there is a group of low-level functions which allow more
  226. direct control of EMS functions. These functions are EMMallocpages(),
  227. EMMgetframeaddr(), EMMgetnumframe(), EMMgetsinfraddr(), and
  228. EMMmappage(). With these functions it is possible to duplicate the
  229. copying functions or access expanded memory in other ways. Calls to
  230. these functions can be mixed with calls to copying functions without
  231. trouble.
  232.  
  233.    Fourth, there is a group of miscellaneous functions: EMMgetname(),
  234. EMMsetname(), EMMgetversion(), EMMsave(), and EMMrestore().
  235.  
  236.    Finally, there is a group of functions which affect only the
  237. operation of EMSLIB itself: _EMMenc(), _EMMdisc(), _EMMinval(),
  238. EMMlibinit(), and EMMsrinit(). For details on all the functions listed
  239. above, see section IV.2.
  240.  
  241.    To use expanded memory, first it is necessary to allocate some. This
  242. can be done with either EMMalloc(), which takes a size in bytes, or
  243. EMMallocpages(), which allocates EMS pages directly. Both return a
  244. handle which will be used to reference the allocated memory.
  245.  
  246.    To access the expanded memory, you can either use the copying
  247. functions, or access it directly. The copying functions are pretty
  248. self-explanatory. Accessing expanded memory directly is more
  249. complicated. First, it is necessary to determine the addresses of the
  250. EMS page frames. EMS page frames 0 through 3 are available under all EMS
  251. versions and are intended for general use. While EMS version 4.0
  252. provides additional page frames, they are intended for use by
  253. multitaskers such as Quarterdeck DesqView and Microsoft Windows. These
  254. additional page frames may appear anywhere, even in the middle of your
  255. program. Use them only with great caution. To obtain the addresses of
  256. all page frames, use EMMgetframeaddr(). To obtain the address of a
  257. single page frame, use EMMgetsinfraddr(). The addresses returned by both
  258. these functions are segment addresses and must be converted to far
  259. pointers before actually being used.
  260.  
  261.    Having decided which page frame you wish to use, and determined where
  262. that page frame is located, it is then necessary to map an EMS logical
  263. page into that page frame. This is accomplished with EMMmappage(). Once
  264. this is done, you can then access any byte in that logical page (which
  265. is 16384 bytes long) using the far pointer created earlier.
  266.  
  267.    The miscellaneous functions listed above allow you to determine the
  268. EMS version supported by the driver (EMMgetversion()), to associate a
  269. string name with an EMS handle and retrieve the name associated with an
  270. EMS handle (EMMsetname()/EMMgetname()), and save and restore EMS page
  271. mappings (EMMsave()/EMMrestore()). More information on the last is
  272. available in section III.4.
  273.  
  274.    The internal functions _EMMenc(), _EMMdisc(), and _EMMinval() are
  275. discussed in section III.3 below. EMMlibinit() has already been
  276. discussed, in section III.1, and EMMsrinit() is discussed in section
  277. III.4 below.
  278.  
  279.    Many functions return a status directly. For those functions, a
  280. return value of 0 indicates success and a return value of EMMOOPS
  281. indicates failure. The global variable _EMMerror contains an error code
  282. which gives further information on why the function failed, or has value
  283. 0 if the function succeeded. Many functions return some other value
  284. instead of a status code (0 or EMMOOPS). In these cases, the value of
  285. _EMMerror should be checked upon return. The recommended method of
  286. error-checking is indicated for each function in section IV.2 below.
  287.  
  288.  III.3 FRAME CACHING
  289.  -------------------
  290.  
  291.    EMSLIB uses a technique called "frame caching" to speed up the
  292. copying functions. It keeps track of the handle and page number of the
  293. logical page currently mapped into page frame 2. When starting a copy,
  294. EMSLIB calculates the handle and logical page needed, and if that page
  295. is already mapped into page frame 2, does not perform a mapping call.
  296. This technique can greatly increase the copying speed, especially if
  297. many separate copies to the same page are being performed.
  298.  
  299.    Frame caching is on by default. To check the current setting, inspect
  300. the global variable _EMMframecache. If it is nonzero, frame caching is
  301. on; if it is 0, frame caching is off. The state of frame caching is
  302. changed via the functions _EMMenc() (enable caching) and _EMMdisc()
  303. (disable caching).
  304.  
  305.    Frame caching has one unfortunate weakness. It assumes that all EMS
  306. mapping activity will be performed via EMSLIB, so that an accurate
  307. record of the contents of frame 2 can be kept. If you are using another
  308. third-party library that also uses EMS, then obviously there is mapping
  309. activity that is not performed via EMSLIB, and frame caching will cause
  310. the copying functions to malfunction.
  311.  
  312.    This is why _EMMenc() and _EMMdisc() were created. If you have this
  313. problem, you have a number of options:
  314.  
  315.         1) You can disable frame caching globally. Just call _EMMdisc()
  316.            somewhere between the EMMlibinit() call and the first call to
  317.            an EMSLIB copying function. If you mostly do large copies,
  318.            especially copies which cross page boundaries, then there
  319.            will be almost no performance loss. If you do lots of
  320.            separate copies to the same page, performance may drop by up
  321.            to 50% or more. In that case, you should probably pick
  322.            another option.
  323.  
  324.         2) You can disable frame caching around calls to the other
  325.            library. If the calls to the other library occur in clusters,
  326.            you can call _EMMdisc() before the cluster and _EMMenc()
  327.            after. Alternatively, if the calls to EMSLIB form more
  328.            convenient clusters, you can call _EMMenc() before and
  329.            _EMMdisc() after. Note that _EMMdisc() sets a flag which
  330.            invalidates the current cache contents, so that when caching
  331.            is re-enabled EMSLIB does not become confused.
  332.  
  333.         3) If calls to the other library are sparse, you can simply
  334.            invalidate the cache each time you make a call to the other
  335.            library, using the macro _EMMinval(). It does not matter
  336.            whether you call _EMMinval() before or after the call to the
  337.            other library, as long as the cache is invalidated before the
  338.            next EMSLIB copying call is made.
  339.  
  340.  III.4 SAVE/RESTORE
  341.  ------------------
  342.  
  343.    Worse interactions with other libraries can occur. If the other
  344. library assumes that the EMS mapping will not change between calls to
  345. it, then almost any EMSLIB activity at all will interfere with the other
  346. library. This is why save/restore capability was added to EMSLIB. Note
  347. that save/restore only works with EMS version 3.2 and up. While EMS
  348. version 3.0 claimed to have save/restore capability, it is so radically
  349. different from that in versions 3.2 and up that it cannot be
  350. consolidated under the same interface.
  351.  
  352.    The EMS specification (for versions 3.2 and up) includes calls to
  353. save the current EMS mapping to a buffer in memory, and restore a
  354. mapping from such a buffer. EMSLIB's save/restore function are an
  355. interface to these calls. Before any save/restore calls can be made,
  356. EMMsrinit() must be called to initialize save/restore. It takes a single
  357. parameter, which is a pointer to a function used to allocate save
  358. buffers. Malloc() may be passed in any memory model, but any other
  359. function with the same prototype may be used. Note that the passed
  360. function will never be called to allocate more that 255 bytes in one
  361. call.
  362.  
  363.    Having initialized save/restore, you can call EMMsave() to save a
  364. mapping and EMMrestore() to restore one. The pointer returned by
  365. EMMsave() is the value returned by an internal call to the function
  366. which was passed to EMMsrinit(), and may be freed when necessary with
  367. whatever function would ordinarily be called; for example, if malloc()
  368. was passed to EMMsrinit(), then pointers returned by EMMsave() may be
  369. passed to free(). Note that EMMrestore() does not free save buffers. You
  370. must free them yourself.
  371.  
  372.    To avoid interference with libraries that assume that the EMS mapping
  373. will not change, simply call EMMsave() after calls to that library, and
  374. call EMMrestore() with the pointer returned by the last EMMsave() before
  375. the next call to that library. Note that EMMrestore() invalidates the
  376. frame cache. In fact, if you have to use save/restore in this way, it
  377. would probably be best to disable frame caching globally, perhaps
  378. enabling it around loops which perform lots of copies if you really need
  379. the performance boost.
  380.  
  381.    Save/restore can also be used to quickly alternate between several
  382. complex mappings. Having established a mapping, save it with EMMsave().
  383. Then, when you need it again, a call to EMMrestore() will restore it
  384. quickly and easily.
  385.  
  386.  III.5 OTHER TIPS
  387.  ----------------
  388.  
  389.    Expanded memory is not deallocated automatically when a program
  390. exits. If the program does not deallocate expanded memory it has
  391. allocated, that expanded memory is stuck, not available to any other
  392. program until the machine is rebooted. Under normal circumstances, it is
  393. easy enough to free expanded memory when it is no longer needed.
  394. However, an emergency exit due to the user hitting control-break or due
  395. to a hardware error (e.g. the famous "Abort, Retry, Fail?") can cause
  396. expanded memory to become stuck unless your program takes special
  397. measures to intercept these errors and perform cleanup before exiting.
  398. There are a number of ways to do this and they vary from compiler to
  399. compiler. Look for functions named things like ctrlbrk(), harderr() and
  400. signal().
  401.  
  402.    If you are copying array elements, _EMMicopyto()/_EMMicopyfrom() or
  403. EMMicopyto()/EMMicopyfrom() may be more efficient. These functions allow
  404. copying of elements of fixed size which are separated by gaps also of
  405. fixed size. For example, if you have an array of integers and wish to
  406. copy all the even-indexed elements (a[0], a[2], a[4], etc.), these
  407. functions are far faster than calling a standard copying function
  408. (EMMcopyto() or EMMcopyfrom()) from within a loop. While the performance
  409. improvement varies from machine to machine, the smallest speed increase
  410. that I have seen is seven times, ranging up to over twenty times on a
  411. fast machine with frame caching off.
  412.  
  413.  
  414. IV. LIBRARY REFERENCE
  415. ---------------------
  416.  
  417.  IV.1 GLOBAL VARIABLES
  418.  ---------------------
  419.  
  420.   _EMMerror
  421.   ---------
  422.  
  423.    unsigned char const _EMMerror;
  424.  
  425.    _EMMerror contains the error code from the last EMSLIB call. If the
  426. call succeeded, _EMMerror will be 0. If the call failed because the EMS
  427. driver returned an error, _EMMerror contains the error code returned by
  428. the EMS driver. If the call failed because EMSLIB detected an error
  429. internally, _EMMerror contains an intenal error code. A list of error
  430. code values, their meanings, and #defined constants for them is in
  431. section V.
  432.  
  433.   _EMMframecache
  434.   --------------
  435.  
  436.    unsigned char const _EMMframecache;
  437.  
  438.    _EMMframecache is a flag which indicates whether frame caching is
  439. enabled or not. A nonzero value indicates that frame caching is enabled,
  440. and 0 indicates that frame caching is disabled. See section IV.3 for a
  441. discussion of frame caching.
  442.  
  443.   _EMMversion
  444.   -----------
  445.  
  446.    unsigned char const _EMMversion;
  447.  
  448.    _EMMversion contains the EMS version implemented by the EMS driver in
  449. packed BCD format. For example, if the EMS version is 4.0, the value of
  450. _EMMversion will be 0x40. If _EMMversion is 0x32, the EMS version is
  451. 3.2. This value is the same as that returned by EMMgetversion().
  452.  
  453.   emslib_vers_vers, emslib_vers_date, emslib_vers_time
  454.   ----------------------------------------------------
  455.  
  456.    char const emslib_vers_vers[];
  457.    char const emslib_vers_date[];
  458.    char const emslib_vers_time[];
  459.  
  460.    These are null-terminated strings containing information about the
  461. name and version of the library, the date of assembly, and the time of
  462. assembly, respectively.
  463.  
  464.  IV.2 FUNCTIONS
  465.  --------------
  466.  
  467.   _EMMdisc() - disable frame caching
  468.   ----------
  469.  
  470.    void _EMMdisc(void);
  471.    void _EMMenc(void);
  472.    void _EMMinval(void);
  473.  
  474.    _EMMdisc() disables frame caching, invalidates the current cache
  475. contents, and sets _EMMframecache to zero. _EMMerror should be checked
  476. after calling this function. It is not an error to call this function
  477. when frame caching is already disabled.
  478.  
  479.    _EMMenc() enables frame caching, invalidates the current cache
  480. contents, and sets _EMMframecache to a nonzero value. _EMMerror should
  481. be checked after calling this function. It is not an error to call this
  482. function when frame caching is already enabled; however, the current
  483. cache contents will still be invalidated.
  484.  
  485.    _EMMinval() is a macro that invalidates the current cache contents if
  486. frame caching is enabled, or has no effect if frame caching is disabled.
  487. The frame caching status is not changed. _EMMerror should be checked
  488. afterwards.
  489.  
  490.    For more information on frame caching, see section III.3.
  491.  
  492.   _EMMenc() - enable frame caching
  493.   ---------
  494.  
  495.    void _EMMenc(void);
  496.  
  497.    See _EMMdisc();
  498.  
  499.   _EMMicopyfrom() - copy data by intervals from EMS
  500.   ---------------
  501.  
  502.    int _EMMicopyfrom(unsigned long nelem, int elsize,
  503.                      unsigned int srcskip, int handle, unsigned long foffset,
  504.                      unsigned char far *dest, unsigned int destskip);
  505.    int _EMMicopyto(unsigned long nelem, int elsize,
  506.                    unsigned int srcskip, unsigned char far *source,
  507.                    int handle, unsigned long foffset, unsigned int destskip);
  508.  
  509.    _EMMicopyfrom() allows painless copying of array elements from
  510. expanded memory to conventional memory. EMS mapping, calculation of
  511. addresses, skipping of unwanted elements, etc., are all handled by the
  512. function. Elements spread across more than 64K, or totalling more than
  513. 64K in length, can be copied without special treatment.
  514.    Nelem elements are copied, each of which is elsize bytes long. The
  515. elements are copied from the EMS pages owned by handle, starting at byte
  516. offset foffset and with srcskip bytes between elements, to conventional
  517. memory starting at dest with destskip bytes between elements. Dest must
  518. be a far pointer; a near pointer can be converted to a far pointer with
  519. a cast when the function is called. Byte offsets in EMS are as for
  520. EMMcopyfrom().
  521.    If nelem or elsize is zero, the function returns immediately without
  522. error. Elsize must be in the range 0 through 16384, or the function will
  523. return error EMM_ELTOOBIG. Byteskip must be in the range 0 through 32768,
  524. or the function will return EMM_SKTOOBIG.
  525.    This function returns 0 on success or EMMOOPS on error. The specific
  526. error may be determined from _EMMerror.
  527.  
  528.    _EMMicopyto() allows painless copying of array elements from
  529. conventional memory to expanded memory. EMS mapping, calculation of
  530. addresses, skipping of unwanted elements, etc., is handled by the
  531. function. Elements spread across more than 64K, or totalling more than
  532. 64K in length, can be copied without special treatment.
  533.    Nelem elements are copied, each of which is elsize bytes long. The
  534. elements are copied from conventional memory starting at source with
  535. srcskip bytes between elements, to the EMS pages owned by handle, starting
  536. at byte offset foffset and with destskip bytes between elements. Source
  537. must be a far pointer; a near pointer can be converted to a far pointer
  538. with a cast when the function is called. Byte offsets in EMS are as for
  539. EMMcopyto().
  540.    If nelem or elsize is zero, the function returns immediately without
  541. error. Elsize must be in the range 0 through 16384, or the function will
  542. return error EMM_ELTOOBIG. Byteskip must be in the range 0 through
  543. 32768, or the function will return EMM_SKTOOBIG.
  544.    This function returns 0 on success or EMMOOPS on error. The specific
  545. error may be determined from _EMMerror.
  546.  
  547.   _EMMicopyto() - copy data by intervals to EMS
  548.   -------------
  549.  
  550.    int _EMMicopyto(unsigned long nelem, int elsize,
  551.                    unsigned int srcskip, unsigned char far *source,
  552.                    int handle, unsigned long foffset, unsigned int destskip);
  553.  
  554.  
  555.    See _EMMicopyfrom().
  556.  
  557.   _EMMinval() - invalidate frame cache contents
  558.   -----------
  559.  
  560.    void _EMMinval(void);
  561.  
  562.    See _EMMdisc().
  563.  
  564.   EMMalloc() - allocate EMS
  565.   ----------
  566.  
  567.    int EMMalloc(unsigned long bytes);
  568.    int EMMallocpages(int pages);
  569.  
  570.    EMMalloc() allocates EMS memory. It takes the given number of bytes
  571. and allocates the smallest number of pages which contain that many
  572. bytes. It returns the EMS handle assigned by the EMS driver. _EMMerror
  573. should be checked after calling this function.
  574.    Note that EMS cannot be allocated in units smaller than a page (16384
  575. bytes). Requesting one byte will allocate a whole page; 16385 bytes will
  576. allocate two pages. Requesting zero bytes allocates one page.
  577.  
  578.    EMMallocpages() allocates EMS memory by pages directly. It returns
  579. the EMS handle assigned by the EMS driver. _EMMerror should be checked
  580. after calling this function. It is possible to allocate 0 pages with
  581. this function, but only under EMS version 4.0; under earlier versions,
  582. allocating 0 pages is an error.
  583.  
  584.   EMMallocpages() - allocate EMS by pages
  585.   ---------------
  586.  
  587.    int EMMallocpages(int pages);
  588.  
  589.    See EMMalloc().
  590.  
  591.   EMMcopyfrom() - copy data from EMS
  592.   -------------
  593.  
  594.    int EMMcopyfrom(unsigned long copylen,
  595.                    int handle, unsigned long foffset,
  596.                    unsigned char far *dest);
  597.    int EMMcopyto(unsigned long copylen,
  598.                  unsigned char far *source,
  599.                  int handle, unsigned long foffset);
  600.  
  601.    EMMcopyfrom() allows painless copying of blocks of data from expanded
  602. memory to conventional memory. All mapping, calculation of addresses,
  603. etc., is handled by the function. Copies longer than 64K are possible
  604. without special treatment.
  605.    Copylen bytes of data are copied, from the EMS pages owned by handle,
  606. starting at byte offset foffset, to the conventional memory area pointed
  607. to by dest. Dest must be a far pointer; a near pointer can be converted
  608. to a far pointer with a cast when the function is called. To convert an
  609. expanded memory offset (such as foffset) to a page and offset within
  610. that page, divide the offset by the size of a page (16384 bytes). The
  611. quotient is the page number and the remainder is the offset within that
  612. page. Offsets 0 through 16383 are in page 0; offsets 16384 through 32767
  613. are in page 1, etc. This feature allows you to treat expanded memory
  614. blocks as linear memory rather than a collection of pages.
  615.    This function returns 0 on success or EMMOOPS on error. The specific
  616. error may be determined from _EMMerror. If copylen is 0, the function
  617. returns immediately without error.
  618.  
  619.    EMMcopyto() is the mirror image of EMMcopyfrom(). It allows painless
  620. copying of blocks of data from conventional memory to expanded memory.
  621. All mapping, calculation of addresses, etc., is handled by the function.
  622. Copies longer than 64K are possible without special treatment.
  623.    Copylen bytes of data are copied, from the conventional memory area
  624. pointed to by source, to the EMS pages owned by handle, starting at
  625. byte offset foffset. Expanded memory offsets are as in EMMcopyfrom().
  626.    This function returns 0 on success or EMMOOPS on error. The specific
  627. error may be determined from _EMMerror. If copylen is 0, the function
  628. returns immediately without error.
  629.  
  630.    See also EMMicopyto() and EMMicopyfrom().
  631.  
  632.   EMMcopyto() - copy data to EMS
  633.   -----------
  634.  
  635.    int EMMcopyto(unsigned long copylen,
  636.                  unsigned char far *source,
  637.                  int handle, unsigned long foffset);
  638.  
  639.    See EMMcopyfrom().
  640.  
  641.   EMMcoreleft() - get amount of free EMS
  642.   -------------
  643.  
  644.    unsigned long EMMcoreleft(void);
  645.  
  646.    This function returns the amount of EMS memory available, in bytes.
  647. To determine the number of EMS pages available, divide the returned
  648. value by 16384. _EMMerror should be checked after calling this function.
  649.  
  650.   EMMfree() - deallocate EMS
  651.   ---------
  652.  
  653.    int EMMfree(int handle);
  654.  
  655.    This function accepts an EMS handle (as returned by EMMalloc() or
  656. EMMallocpages(), or otherwise obtained from the EMS driver) and releases
  657. it and any EMS pages allocated to it. This function returns 0 on success
  658. or EMMOOPS on error. The specific error may be determined from
  659. _EMMerror.
  660.  
  661.   EMMgetframeaddr() - get EMS page frame segment addresses
  662.   -----------------
  663.  
  664.    int EMMgetframeaddr(frameinfo *buffer);
  665.    int EMMgetnumframe(void);
  666.    unsigned int EMMgetsinfraddr(int frame);
  667.  
  668.    EMMgetframeaddr() returns the segment addresses of all the EMS page
  669. frames. Buffer is a pointer to an array of frameinfo structures; this
  670. array is assumed to be large enough to contain information about all the
  671. frames. The number of frameinfo structures needed can be determined with
  672. the EMMgetnumframe() function described below.
  673.    Each frameinfo structure contains the number and the segment address
  674. of a frame. Check EMSLIB.H or EMSLIB.HPP for the exact format of the
  675. frameinfo structure. The information is returned in whatever order the
  676. EMS driver provides it, which is typically sorted by address rather than
  677. frame number. Do NOT just assume that the first element in the array
  678. contains information for frame 0! The segment addresses returned must be
  679. converted to far pointers (using MK_FP() or your compiler's equivalent)
  680. before they can be used to access memory.
  681.    This function returns 0 on success or EMMOOPS on error. The specific
  682. error may be determined from _EMMerror.
  683.  
  684.    EMMgetnumframe() returns the number of EMS page frames present in the
  685. system. For EMS versions below 4.0, this will always be 4. For EMS
  686. version 4.0, it will be at least 4. _EMMerror should be checked after
  687. calling this function.
  688.  
  689.    EMMgetsinfraddr() returns the segment address of a particular EMS
  690. page frame. This address must be converted to a far pointer (using
  691. MK_FP() or your compiler's eqivalent) before it can be used to access
  692. memory. _EMMerror should be checked after calling this function.
  693.  
  694.   EMMgetname() - obtain string name associated with an EMS handle
  695.   ------------
  696.  
  697.    int EMMgetname(int handle, char *name);
  698.    int EMMsetname(int handle, char *name);
  699.  
  700.    EMMgetname() returns (as a null-terminated string) the name, if any,
  701. assigned to an EMS handle. Real names are only implemented in EMS
  702. version 4.0. Under earlier versions, this function fills the name buffer
  703. with nulls ('\0'), which is also the return under version 4.0 when no
  704. name has been associated with the given handle. The buffer pointed to by
  705. name must be at least nine characters long (since an EMS name may be up
  706. to eight characters long and there must be room for the terminating
  707. null). This function returns 0 on success or EMMOOPS on error. The
  708. specific error may be determined from _EMMerror.
  709.  
  710.    EMMsetname() allows the association of a name up to eight characters
  711. long with an EMS handle. It only works for EMS version 4.0; under
  712. earlier versions, it returns the error EMM_BADVERS. Version independency
  713. has been abandoned in this case because the main purpose of associating
  714. a name with an EMS handle is to allow several different programs to
  715. recognize shared data. While EMSLIB could implement a naming function
  716. within itself, the names would not be visible outside the program
  717. assigning the name, defeating the purpose of doing it in the first
  718. place.
  719.    This function returns 0 on success or EMMOOPS on error. The specific
  720. error may be determined from _EMMerror.
  721.  
  722.   EMMgetnumframe() - obtain number of EMS page frames
  723.   ----------------
  724.  
  725.    int EMMgetnumframe(void);
  726.  
  727.    See EMMgetframeaddr().
  728.  
  729.   EMMgetsinfraddr() - get segment address of one EMS page frame
  730.   -----------------
  731.  
  732.    unsigned int EMMgetsinfraddr(int frame);
  733.  
  734.    See EMMgetframeaddr().
  735.  
  736.   EMMgetversion() - obtain EMS version implemented by EMS driver
  737.   ---------------
  738.  
  739.    int EMMgetversion(void);
  740.  
  741.    This function returns the EMS version implemented by the EMS driver.
  742. The version number is in packed BCD format as in the global variable
  743. _EMMversion, and is in fact the same value. _EMMerror should be checked
  744. after calling this function.
  745.  
  746.   EMMicopyfrom() - copy data by intervals from EMS
  747.   --------------
  748.  
  749.    int EMMicopyfrom(unsigned long nelem, int elsize, unsigned int byteskip,
  750.                     int handle, unsigned long foffset,
  751.                     unsigned char far *dest);
  752.    int EMMicopyto(unsigned long nelem, int elsize, unsigned int byteskip,
  753.                   unsigned char far *source,
  754.                   int handle, unsigned long foffset);
  755.  
  756.    EMMicopyfrom() is a macro which calls _EMMicopyfrom(). It allows
  757. painless copying of array elements from expanded memory to conventional
  758. memory. EMS mapping, calculation of addresses, skipping of unwanted
  759. elements, etc., are all handled by the function. Elements spread across
  760. more than 64K, or totalling more than 64K in length, can be copied without
  761. special treatment.
  762.    Nelem elements are copied, each of which is elsize bytes long, with
  763. byteskip bytes between elements at both source and destination. The
  764. elements are copied from the EMS pages owned by handle, starting at byte
  765. offset foffset, to conventional memory starting at dest. Dest must be a
  766. far pointer; a near pointer can be converted to a far pointer with a cast
  767. when the function is called. Byte offsets in EMS are as for EMMcopyfrom().
  768.    If nelem or elsize is zero, the function returns immediately without
  769. error. Elsize must be in the range 0 through 16384, or the function will
  770. return error EMM_ELTOOBIG. Byteskip must be in the range 0 through
  771. 32768, or the function will return EMM_SKTOOBIG.
  772.    This function returns 0 on success or EMMOOPS on error. The specific
  773. error may be determined from _EMMerror.
  774.  
  775.    EMMicopyto() is a macro which calls _EMMicopyto(). It allows painless
  776. copying of array elements from conventional memory to expanded memory. EMS
  777. mapping, calculation of addresses, skipping of unwanted elements, etc., is
  778. handled by the function. Elements spread across more than 64K, or totalling
  779. more than 64K in length, can be copied without special treatment.
  780.    Nelem elements are copied, each of which is elsize bytes long, with
  781. byteskip bytes between elements at both source and destination. The elements
  782. are copied from conventional memory starting at source, to the EMS pages
  783. owned by handle, starting at byte offset foffset. Source must be a far
  784. pointer; a near pointer can be converted to a far pointer with a cast when
  785. the function is called. Byte offsets in EMS are as for EMMcopyto().
  786.    If nelem or elsize is zero, the function returns immediately without
  787. error. Elsize must be in the range 0 through 16384, or the function will
  788. return error EMM_ELTOOBIG. Byteskip must be in the range 0 through
  789. 32768, or the function will return EMM_SKTOOBIG.
  790.    This function returns 0 on success or EMMOOPS on error. The specific
  791. error may be determined from _EMMerror.
  792.  
  793.   EMMicopyto() - copy data by intervals to EMS
  794.   ------------
  795.  
  796.    int EMMicopyto(unsigned long nelem, int elsize, unsigned int byteskip,
  797.                   unsigned char far *source,
  798.                   int handle, unsigned long foffset);
  799.  
  800.  
  801.    See EMMicopyfrom().
  802.  
  803.   EMMlibinit() - initialize EMSLIB
  804.   ------------
  805.  
  806.    int EMMlibinit(void);
  807.  
  808.    EMMlibinit() initializes EMSLIB. Any other EMSLIB function will
  809. return the error EMM_NOINIT if called before EMMlibinit() has been
  810. called. This function returns 0 on success, EMMOOPS on error, or NOEMM
  811. if no EMS driver is detected. The specific error may be determined from
  812. _EMMerror. Note that EMSLIB will not initialize if the EMS driver claims
  813. that fewer than four EMS page frames exist, since that is a violation of
  814. the EMS specification and the driver may have other anomalies that
  815. EMSLIB does not know how to handle.
  816.    Detection of the EMS driver relies on DOS services, so this library
  817. cannot be used in a device driver as it stands. If you need this library
  818. for a device driver or other program that cannot access DOS services,
  819. you can buy the source (it's cheap) and easily modify EMMlibinit() to
  820. use another method of detection. There are no other calls to DOS
  821. services in EMSLIB. The method used was chosen for its reliability;
  822. there is another method, suitable for use in device drivers, which is
  823. not as reliable and typically is useful _only_ for device drivers.
  824.  
  825.   EMMmappage() - map an EMS logical page into an EMS page frame
  826.   ------------
  827.  
  828.    int EMMmappage(int frameno, int handle, int logpage);
  829.  
  830.    This function maps a logical page into an EMS page frame so that it
  831. can be accessed. The logical page logpage belonging to handle is mapped
  832. into page frame number frameno. This function returns 0 on success or
  833. EMMOOPS on error. The specific error may be determined from _EMMerror.
  834.  
  835.   EMMrestore() - restore an EMS mapping from a save buffer
  836.   ------------
  837.  
  838.    int EMMrestore(void *saveblock);
  839.    void *EMMsave(void);
  840.    int EMMsrinit(void *(*mallocfunc)(size_t));
  841.  
  842.    EMMrestore() restores an EMS mapping that has earlier been saved in a
  843. save buffer with a call to EMMsave(). The contents of the frame cache
  844. are invalidated, since the format of the contents of the save buffer
  845. varies from one EMS driver to the next and it is thus not possible to
  846. determine what will be mapped into the cached frame when the restore is
  847. done. EMMrestore() does not deallocate the save buffer.
  848.    This function returns 0 on success or EMMOOPS on error. The specific
  849. error may be determined from _EMMerror. It is an error if this function
  850. is called before EMMsrinit() has been called.
  851.  
  852.    EMMsave() allocates a save buffer (see EMMsrinit(), below) and saves
  853. the current EMS mapping into it. The current EMS mapping is not changed,
  854. nor are the contents of the frame cache invalidated. This function
  855. returns the pointer returned by the allocation function (see below).
  856.    _EMMerror should be checked after calling this function. It is an
  857. error if this function is called before EMMsrinit() has been called.
  858.  
  859.    EMMsrinit() initializes the save/restore capability of EMSLIB. It
  860. takes a pointer to a memory-allocation function which is used by
  861. EMMsave() to allocate save buffers. Malloc() is compatible in all memory
  862. models, but any function which works the same way (returns a void
  863. pointer of the size normal for the memory model (near or far), takes a
  864. single parameter of type size_t which is the number of bytes to
  865. allocate, and returns NULL (0) if the memory cannot be allocated) can
  866. also be used. Note that the function will never have to allocate more
  867. than 255 bytes in one call
  868.    EMMsrinit() will fail to initialize and return EMM_BADVERS if the EMS
  869. version is 3.0. Since it did not initialize, EMMsave() and EMMrestore()
  870. will not work either.
  871.    _EMMerror should be checked after calling this function.
  872.  
  873.    For more information on frame caching and suggestions on using
  874. save/restore, see sections III.3 and III.4 respectively.
  875.  
  876.   EMMsave() - save an EMS mapping into a save buffer
  877.   ---------
  878.  
  879.    void *EMMsave(void);
  880.  
  881.    See EMMrestore().
  882.  
  883.   EMMsetname() - associate string name with an EMS handle
  884.   ------------
  885.  
  886.    int EMMsetname(int handle, char *name);
  887.  
  888.    See EMMgetname().
  889.  
  890.   EMMsrinit() - initialize save/restore
  891.   -----------
  892.  
  893.    int EMMsrinit(void *(*mallocfunc)(size_t));
  894.  
  895.    See EMMrestore().
  896.  
  897.  
  898. V. ERROR CODES
  899. --------------
  900.  
  901.    This section is a list of the error codes to which the global variable
  902. _EMMerror may be set, and the values and meanings thereof.
  903.  
  904.  V.1 INTERNAL ERRORS
  905.  -------------------
  906.  
  907.    Internal codes indicate an error detected by EMSLIB itself.
  908.  
  909.    NAME                 VALUE     MEANING
  910.    ----                 -----     -------
  911.    EMM_BADVERS           0x40     Bad EMS version, earlier than 3.0.
  912.  
  913.    EMM_BADOFFSET         0x41     Offset plus copy length runs off last
  914.                                   EMS page owned by handle.
  915.  
  916.    EMM_NOFRAME           0x42     Could not find segment address of frame
  917.                                   used for copying functions.
  918.  
  919.    EMM_NOINIT            0x43     EMMlibinit() has not been called yet.
  920.  
  921.    EMM_FEWFRAMES         0x44     The EMS driver claims that there are fewer
  922.                                   than four page frames.
  923.  
  924.    EMM_NOSR              0x45     EMMsrinit() must be called before
  925.                                   EMMsave() or EMMrestore() can be called.
  926.  
  927.    EMM_MEMNULL           0x46     The save/restore memory allocation
  928.                                   function (the one passed to EMMsrinit())
  929.                                   returned NULL.
  930.  
  931.    EMM_ELTOOBIG          0x47     The element size passed to EMMicopyto()
  932.                                   or EMMicopyfrom() is too big.
  933.  
  934.    EMM_SKTOOBIG          0x48     The skip size passed to EMMicopyto()
  935.                                   or EMMicopyfrom() is too big.
  936.  
  937.  V.2 EMS DRIVER ERRORS
  938.  ---------------------
  939.  
  940.    These codes are defined in the EMS specification and are returned by
  941. the EMS driver. They are saved in _EMMerror by EMSLIB without alteration.
  942.  
  943.    NAME                 VALUE     MEANING
  944.    ----                 -----     -------
  945.    EMM_SOFTERROR         0x80     Internal error exists in EMS driver (can
  946.                                   indicate corrupted memory image of driver)
  947.  
  948.    EMM_HARDERROR         0x81     Malfunction in expanded memory hardware
  949.  
  950.    EMM_BUSY              0x82     EMS driver is busy
  951.  
  952.    EMM_BADHANDLE         0x83     Invalid EMS handle
  953.  
  954.    EMM_UNIMP             0x84     Function not defined
  955.  
  956.    EMM_NOFREEHAN         0x85     No free EMS handles
  957.  
  958.    EMM_CONTEXTERR        0x86     Error in save or restore of mapping context
  959.  
  960.    EMM_WAYTOOBIG         0x87     Tried to allocate more pages than physically
  961.                                   exist in system; no pages allocated
  962.  
  963.    EMM_TOOBIG            0x88     Tried to allocate more pages than currently
  964.                                   available; no pages allocated
  965.  
  966.    EMM_TOOSMALL          0x89     Cannot allocate 0 pages
  967.  
  968.    EMM_BADLOGPAGE        0x8A     Requested logical page is outside range of
  969.                                   pages owned by handle
  970.  
  971.    EMM_BADFRAMENO        0x8B     Illegal frame number in mapping request
  972.  
  973.    EMM_HSTATESAVFULL     0x8C     Page-mapping hardware-state save area full
  974.  
  975.    EMM_MSTATESAVFULL     0x8D     Mapping-context save failed; save area
  976.                                   already contains context associated with
  977.                                   specified handle
  978.  
  979.    EMM_MSTATERESTERR     0x8E     Mapping-context restore failed; save area
  980.                                   does not contain context for specified
  981.                                   handle
  982.  
  983.    EMM_UNIMPSUB          0x8F     Subfunction parameter not defined
  984.  
  985.    EMM_BADATTRIB         0x90     Attribute type not defined
  986.  
  987.    EMM_NOFEATURE         0x91     Feature not supported
  988.  
  989.    EMM_SRCOVERWRITE      0x92     Source and destination memory regions have
  990.                                   same handle and overlap; requested move
  991.                                   was performed, but part of the source region
  992.                                   was overwritten
  993.  
  994.    EMM_BADLENGTH         0x93     Copy length longer than actual length
  995.  
  996.    EMM_CONEMSOVERLAP     0x94     Conventional memory region and expanded
  997.                                   memory region overlap
  998.  
  999.    EMM_OFFPAGE           0x95     Specified offset outside logical page
  1000.  
  1001.    EMM_TOOLONG           0x96     Copy length exceeds one megabyte
  1002.  
  1003.    EMM_EMSEMSOVERLAP     0x97     Source and destination memory regions have
  1004.                                   same handle and overlap; exchange cannot be
  1005.                                   performed
  1006.  
  1007.    EMM_LOST              0x98     Memory source and destination types
  1008.                                   undefined
  1009.  
  1010.    EMM_UNUSED            0x99     This error code is currently unused
  1011.  
  1012.    EMM_BADALTREG         0x9A     Alternate map or DMA register sets are
  1013.                                   supported, but specified alternate register
  1014.                                   set is not supported
  1015.  
  1016.    EMM_NOFREEALTREG      0x9B     Alternate map or DMA register sets are
  1017.                                   supported, but all alternate register sets
  1018.                                   are currently allocated
  1019.  
  1020.    EMM_NOALTREG          0x9C     Alternate map or DMA register sets are not
  1021.                                   supported, specified alternate register
  1022.                                   set is not 0
  1023.  
  1024.    EMM_BADALTREG2        0x9D     Alternate map or DMA register sets are
  1025.                                   supported, but the alternate register set
  1026.                                   specified is not defined or not allocated
  1027.  
  1028.    EMM_NODEDDMA          0x9E     Dedicated DMA channels are not supported
  1029.  
  1030.    EMM_BADDEDDMA         0x9F     Dedicated DMA channels are supported, but
  1031.                                   specified DMA channel is not supported
  1032.  
  1033.    EMM_UNKNAME           0xA0     Handle for specified name not found
  1034.  
  1035.    EMM_NAMETAKEN         0xA1     Handle with same name already exists
  1036.  
  1037.    EMM_ADDRWRAP          0xA2     Memory address wraps; sum of source or
  1038.                                   destination region base address and length
  1039.                                   exceeds one megabyte
  1040.  
  1041.    EMM_BADPTR            0xA3     Invalid pointer passed to function, or
  1042.                                   contents of source array corrupted
  1043.  
  1044.    EMM_FORBIDDENFUNC     0xA4     Access to function denied by operating
  1045.                                   system
  1046.  
  1047. VI. THE END
  1048. -----------
  1049.  
  1050.    Technical support via email is available from the following addresses:
  1051.  
  1052.    INTERNET:
  1053.       First choice (the following are alternate addresses for the same place):
  1054.          support@picarefy.com [coming soon -- try it, anyway]
  1055.          picarefy!support@amc.com
  1056.          picarefy!support@netcom.com
  1057.          picarefy!support@halcyon.com
  1058.          amc-gw!picarefy!support@coco.ms.washington.edu
  1059.          halcyon!picarefy!support@sumax.seattleu.edu
  1060.          uunet!uw-coco!amc-gw!picarefy!support
  1061.  
  1062.       Second choice:
  1063.          jwbirdsa@amc.com
  1064.  
  1065.    COMPUSERVE:
  1066.       71261,1731
  1067.  
  1068.    AMERICA ON-LINE:
  1069.       GreenTiger
  1070.  
  1071.    GENIE:
  1072.       J.BIRDSALL2
  1073.  
  1074.    Registrations should be sent to:
  1075.  
  1076.       James W. Birdsall
  1077.       11112 NE 124 LN #D204
  1078.       Kirkland, WA 98034
  1079.  
  1080.    If you have an email address on any of the networks listed above,
  1081. please include it when registering, especially if you are requesting
  1082. source code. It is much easier to send the source code by email. Also,
  1083. please specify what sort of archive (ZIP, ZOO, ARC, LZH, ARJ, UNIX shar)
  1084. you can handle most easily.
  1085.  
  1086.  VI.1 ACKNOWLEDGEMENTS
  1087.  ---------------------
  1088.  
  1089.    Thanks to Bob Parsons of Parsons Technology Inc. for some good suggestions
  1090. on the documentation and for providing the original C++ header file.
  1091.  
  1092.